package github.ziwukun.jsonql.exe;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import github.ziwukun.jsonql.lang.CmpOpr;
import github.ziwukun.jsonql.lang.Limit;
import github.ziwukun.jsonql.lang.PropSort;
import github.ziwukun.jsonql.lang.Select;
import github.ziwukun.jsonql.lang.SelectItem;
import github.ziwukun.jsonql.lang.Sort;
import github.ziwukun.jsonql.lang.SortSym;
import github.ziwukun.jsonql.lang.Where;

/**JsonQL转SQL管理器
 * @author Hugo.Yu
 */
class JQLToSqlMgr {
	private final static Map<CmpOpr,SymbolToSql> SymbolToSqlMap = new HashMap<>();
	private JQLToSqlMgr() {}
	// 初始化
	static {
		SymbolToSqlMap.put(CmpOpr.AND, new AndToSql());
		SymbolToSqlMap.put(CmpOpr.OR, new OrToSql());
		SymbolToSqlMap.put(CmpOpr.GT, new GtToSql());
		SymbolToSqlMap.put(CmpOpr.LT, new LtToSql());
		SymbolToSqlMap.put(CmpOpr.GTE, new GteToSql());
		SymbolToSqlMap.put(CmpOpr.LTE, new LteToSql());
		SymbolToSqlMap.put(CmpOpr.EQ, new EqToSql());
		SymbolToSqlMap.put(CmpOpr.NEQ, new NeqToSql());
		SymbolToSqlMap.put(CmpOpr.IN, new InToSql());
		SymbolToSqlMap.put(CmpOpr.NIN, new NinToSql());
		SymbolToSqlMap.put(CmpOpr.ISNULL, new IsNullToSql());
		SymbolToSqlMap.put(CmpOpr.NOTNULL, new IsNotNullToSql());
	}
	
	public static String selectToSql(Select select) {
		// 设置SELECT选项
		List<SelectItem> selectColItems = select.listItems();
		List<String> cols = selectColItems.stream().map(selectItem -> {
			return selectItem.prop();
		}).collect(Collectors.toList());
		
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < cols.size(); i++) {
			if(i != cols.size() - 1) {
				sb.append("`").append(cols.get(i)).append("`,");
			}
			else {
				sb.append("`").append(cols.get(i)).append("`");
			}
		}
		
		return sb.toString();
	}
	
	public static String whereToSql(Where where,List<Object> params) {
		StringBuilder sb = new StringBuilder();
		// 逻辑连接符
		if(where.keyIsLogic()) {
			sb.append(JQLToSqlMgr.
				SymbolToSqlMap.get(where.keyAsCmpOpr()).whereToSql(where, params));
		}
		else if(where.keyIsProp()) {
			sb.append("`").append(where.keyAsProp()).append("`")
				.append(SymbolToSqlMap.get(where.valAsWhere().keyAsCmpOpr())
				.whereToSql(where.valAsWhere(), params));
		}
		return sb.toString();
	}
	
	public static String limitToSql(Limit limit) {
		StringBuilder sb = new StringBuilder();
		if(!limit.isChildsEmpty()) {
			throw new RuntimeException("MySQL不支持子节点限制条件");
		}
		// 使用offset方式
		else if(limit.offset() != null) {
			sb.append("LIMIT ").append(limit.offset())
			.append(" , ")
			.append(limit.size());
		}
		else if(limit.page() != null) {
			// 计算offset
			int offset = (limit.page() - 1) * limit.size();
			sb.append("LIMIT ").append(offset)
			.append(" , ")
			.append(limit.size());
		}
		else {
			throw new RuntimeException("错误的@Limit状态");
		}
		
		return sb.toString();
	}
	public static String sortToSql(Sort sort) {
		StringBuilder sb = new StringBuilder();
		
		List<PropSort> propSorts = sort.propSorts();
		for (int i = 0; i < propSorts.size(); i++) {
			PropSort ps  = propSorts.get(i);
			if(ps.valIsSort()) {
				throw new RuntimeException("MySQL不支持子节点排序条件");
			}
			String col = ps.prop();
			SortSym symbol = ps.valAsSym();
			sb.append(col).append(" ").append(symbol);
			
			if(i != propSorts.size() - 1) {
				sb.append(",");
			}else {
				sb.append(" ");
			}
		}
		
		return sb.toString();
	}
	
	/**符号转SQL
	 * @author Hugo.Yu
	 */
	static interface SymbolToSql{
		String whereToSql(Where where,List<Object> params);
	}
	
	/**
	 * AND符号转SQL
	 * @author Hugo.Yu
	 */
	static class AndToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			// 检查
			if(!where.keyIsAnd()) {
				throw new RuntimeException("where语句错误");
			}
			
			// 获取值以便于后续操作
			List<Where> andWheres = where.valAsWheres();
			
			// 拼接sql
			StringBuilder sb = new StringBuilder("(");
			for (int i = 0; i < andWheres.size(); i++) {
				Where andWhere = andWheres.get(i);
				String sql = JQLToSqlMgr.whereToSql(andWhere,params);
				
				if(i != (andWheres.size() - 1) ) {
					sb.append(sql).append(" AND ");
				}
				else {
					sb.append(sql);
				}
			}
			sb.append(")");
			
			return sb.toString();
		}
	}
	
	static class OrToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			// 检查
			if(!where.keyIsOr()) {
				throw new RuntimeException("where语句错误");
			}
			
			// 获取值以便于后续操作
			List<Where> orWheres = where.valAsWheres();
			
			// 拼接sql
			StringBuilder sb = new StringBuilder("(");
			for (int i = 0; i < orWheres.size(); i++) {
				Where orWhere = orWheres.get(i);
				String sql = JQLToSqlMgr.whereToSql(orWhere,params);
				
				if(i != orWheres.size() -1 ) {
					sb.append(sql).append(" OR ");
				}
				else {
					sb.append(sql);
				}
			}
			sb.append(")");
			return sb.toString();
		}
	}
	
	static class GtToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.GT) {
				throw new RuntimeException("where语句错误");
			}
			
			params.add(where.value());
			StringBuilder sb = new StringBuilder(" > ?");
			return sb.toString();
		}
		
	}
	
	static class LtToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.LT) {
				throw new RuntimeException("where语句错误");
			}
			
			params.add(where.value());
			StringBuilder sb = new StringBuilder(" < ?");
			return sb.toString();
		}
		
	}
	
	static class GteToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.GTE) {
				throw new RuntimeException("where语句错误");
			}
			
			params.add(where.value());
			StringBuilder sb = new StringBuilder(" >= ?");
			return sb.toString();
		}
		
	}
	
	static class LteToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.LTE) {
				throw new RuntimeException("where语句错误");
			}
			
			params.add(where.value());
			StringBuilder sb = new StringBuilder(" <= ?");
			return sb.toString();
		}
		
	}
	
	static class EqToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.EQ) {
				throw new RuntimeException("where语句错误");
			}
			
			params.add(where.value());
			StringBuilder sb = new StringBuilder(" = ?");
			return sb.toString();
		}
	}
	
	static class NeqToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.NEQ) {
				throw new RuntimeException("where语句错误");
			}
			
			params.add(where.value());
			StringBuilder sb = new StringBuilder(" != ?");
			return sb.toString();
		}
	}
	
	static class InToSql implements SymbolToSql{

		@Override
		public String whereToSql(Where where, List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.IN) {
				throw new RuntimeException("where语句错误");
			}
			
			@SuppressWarnings("unchecked")
			List<Comparable<?>> values = (List<Comparable<?>>) where.value();
			StringBuilder sb = new StringBuilder(" IN (");
			for (int i = 0; i < values.size(); i++) {
				if(i != values.size() - 1) {
					sb.append(" ?, ");
				}
				else {
					sb.append(" ?");
				}
				params.add(values.get(i));
			}
			sb.append(")");
			
			return sb.toString();
		}
		
	}
	
	
	static class NinToSql implements SymbolToSql{

		@Override
		public String whereToSql(Where where, List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.NIN) {
				throw new RuntimeException("where语句错误");
			}
			
			@SuppressWarnings("unchecked")
			List<Comparable<?>> values = (List<Comparable<?>>) where.value();
			StringBuilder sb = new StringBuilder(" NOT IN (");
			for (int i = 0; i < values.size(); i++) {
				if(i != values.size() - 1) {
					sb.append(" ?, ");
				}
				else {
					sb.append(" ?");
				}
				params.add(values.get(i));
			}
			sb.append(")");
			
			return sb.toString();
		}
		
	}
	
	
	static class IsNullToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.ISNULL) {
				throw new RuntimeException("where语句错误");
			}
			
			params.add(where.value());
			StringBuilder sb = new StringBuilder(" IS NULL");
			return sb.toString();
		}
	}
	
	static class IsNotNullToSql implements SymbolToSql{
		@Override
		public String whereToSql(Where where,List<Object> params) {
			if(!where.keyIsCmpOpr() || where.keyAsCmpOpr() != CmpOpr.NOTNULL) {
				throw new RuntimeException("where语句错误");
			}
			
			params.add(where.value());
			StringBuilder sb = new StringBuilder(" IS NOT NULL");
			return sb.toString();
		}
	}
}
